React์ useActionState ํ ์ ๋ง์คํฐํ์ธ์. ์ค์ฉ์ ์ธ ์์ ๋ฅผ ํตํด ํผ ๊ด๋ฆฌ ๋จ์ํ, ๋ณด๋ฅ ์ํ ์ฒ๋ฆฌ, ์ฌ์ฉ์ ๊ฒฝํ ํฅ์ ๋ฐฉ๋ฒ์ ์ฌ๋ ์๊ฒ ๋ค๋ฃน๋๋ค.
React useActionState: ์ต์ ํผ ๊ด๋ฆฌ๋ฅผ ์ํ ์ข ํฉ ๊ฐ์ด๋
์น ๊ฐ๋ฐ์ ์ธ๊ณ๋ ๋์์์ด ์งํํ๊ณ ์์ผ๋ฉฐ, ๋ฆฌ์กํธ ์ํ๊ณ๋ ์ด๋ฌํ ๋ณํ์ ์ต์ ์ ์ ์์ต๋๋ค. ์ต๊ทผ ๋ฒ์ ์์ ๋ฆฌ์กํธ๋ ์ํธ์์ฉ์ ์ด๊ณ ํ๋ ฅ์ ์ธ ์ ํ๋ฆฌ์ผ์ด์ ์ ๊ตฌ์ถํ๋ ๋ฐฉ์์ ๊ทผ๋ณธ์ ์ผ๋ก ๊ฐ์ ํ๋ ๊ฐ๋ ฅํ ๊ธฐ๋ฅ๋ค์ ๋์ ํ์ต๋๋ค. ๊ทธ์ค ๊ฐ์ฅ ์ํฅ๋ ฅ ์๋ ๊ฒ ์ค ํ๋๊ฐ ๋ฐ๋ก ํผ๊ณผ ๋น๋๊ธฐ ์์ ์ ์ฒ๋ฆฌํ๋ ๋ฐฉ์์ ํ๊ธฐ์ ์ผ๋ก ๋ฐ๊พผ useActionState ํ ์ ๋๋ค. ์ด ํ ์ ์คํ์ ๋ฆด๋ฆฌ์ค์์ useFormState๋ก ์๋ ค์ก์์ง๋ง, ์ด์ ๋ ๋ชจ๋ ํ๋ ๋ฆฌ์กํธ ๊ฐ๋ฐ์์๊ฒ ์์ ์ ์ด๊ณ ํ์์ ์ธ ๋๊ตฌ๊ฐ ๋์์ต๋๋ค.
์ด ์ข ํฉ ๊ฐ์ด๋๋ useActionState์ ๋ํด ๊น์ด ์๊ฒ ํ๊ตฌํฉ๋๋ค. ์ด ํ ์ด ํด๊ฒฐํ๋ ๋ฌธ์ ๋ค, ํต์ฌ ๋ฉ์ปค๋์ฆ, ๊ทธ๋ฆฌ๊ณ useFormStatus์ ๊ฐ์ ๋ณด์์ ์ธ ํ ๋ค๊ณผ ํจ๊ป ํ์ฉํ์ฌ ์ฐ์ํ ์ฌ์ฉ์ ๊ฒฝํ์ ๋ง๋๋ ๋ฐฉ๋ฒ์ ์์๋ณผ ๊ฒ์ ๋๋ค. ๊ฐ๋จํ ์ฐ๋ฝ์ฒ ํผ์ ๋ง๋ค๋ , ๋ณต์กํ๊ณ ๋ฐ์ดํฐ ์ง์ฝ์ ์ธ ์ ํ๋ฆฌ์ผ์ด์ ์ ๋ง๋ค๋ , useActionState๋ฅผ ์ดํดํ๋ฉด ์ฝ๋๊ฐ ๋ ๊น๋ํ๊ณ , ์ ์ธ์ ์ด๋ฉฐ, ๊ฒฌ๊ณ ํด์ง ๊ฒ์ ๋๋ค.
๋ฌธ์ ์ : ์ ํต์ ์ธ ํผ ์ํ ๊ด๋ฆฌ์ ๋ณต์ก์ฑ
useActionState์ ์ฐ์ํจ์ ์ดํดํ๊ธฐ ์ ์, ๋จผ์ ์ด ํ ์ด ํด๊ฒฐํ๋ ๋ฌธ์ ๋ค์ ์ดํดํด์ผ ํฉ๋๋ค. ์๋ ๊ฐ ๋ฆฌ์กํธ์์ ํผ ์ํ๋ฅผ ๊ด๋ฆฌํ๋ ๊ฒ์ useState ํ ์ ์ฌ์ฉํ๋ ์์ธก ๊ฐ๋ฅํ์ง๋ง ์ข ์ข ๋ฒ๊ฑฐ๋ก์ด ํจํด์ ํฌํจํ์ต๋๋ค.
๊ฐ๋จํ ์์๋ก, ๋ชฉ๋ก์ ์ ์ ํ์ ์ถ๊ฐํ๋ ํผ์ ์๊ฐํด ๋ด ์๋ค. ์ฐ๋ฆฌ๋ ์ฌ๋ฌ ์ํ๋ฅผ ๊ด๋ฆฌํด์ผ ํฉ๋๋ค:
- ์ ํ ์ด๋ฆ์ ์ ๋ ฅ ๊ฐ.
- API ํธ์ถ ์ค ์ฌ์ฉ์์๊ฒ ํผ๋๋ฐฑ์ ์ฃผ๊ธฐ ์ํ ๋ก๋ฉ ๋๋ ๋ณด๋ฅ ์ํ.
- ์ ์ถ์ด ์คํจํ ๊ฒฝ์ฐ ๋ฉ์์ง๋ฅผ ํ์ํ๊ธฐ ์ํ ์ค๋ฅ ์ํ.
- ์๋ฃ ์ ์ฑ๊ณต ์ํ ๋๋ ๋ฉ์์ง.
์ผ๋ฐ์ ์ธ ๊ตฌํ์ ๋ค์๊ณผ ๊ฐ์ ์ ์์ต๋๋ค:
์์: ์ฌ๋ฌ useState ํ ์ ์ฌ์ฉํ๋ '๊ตฌ์' ๋ฐฉ๋ฒ
// ๊ฐ์์ API ํจ์
const addProductAPI = async (productName) => {
await new Promise(resolve => setTimeout(resolve, 1500));
if (!productName || productName.length < 3) {
throw new Error('์ ํ๋ช
์ 3๊ธ์ ์ด์์ด์ด์ผ ํฉ๋๋ค.');
}
console.log(`"${productName}" ์ ํ์ด ์ถ๊ฐ๋์์ต๋๋ค.`);
return { success: true };
};
// ์ปดํฌ๋ํธ
{error}import { useState } from 'react';
function OldProductForm() {
const [productName, setProductName] = useState('');
const [error, setError] = useState(null);
const [isPending, setIsPending] = useState(false);
const handleSubmit = async (event) => {
event.preventDefault();
setIsPending(true);
setError(null);
try {
await addProductAPI(productName);
setProductName(''); // ์ฑ๊ณต ์ ์
๋ ฅ ๋น์ฐ๊ธฐ
} catch (err) {
setError(err.message);
} finally {
setIsPending(false);
}
};
return (
id="productName"
name="productName"
value={productName}
onChange={(e) => setProductName(e.target.value)}
/>
{isPending ? '์ถ๊ฐ ์ค...' : '์ ํ ์ถ๊ฐ'}
{error &&
);
}
์ด ์ ๊ทผ ๋ฐฉ์์ ์๋ํ์ง๋ง ๋ช ๊ฐ์ง ๋จ์ ์ด ์์ต๋๋ค:
- ๋ณด์ผ๋ฌํ๋ ์ดํธ: ๊ฐ๋ ์ ์ผ๋ก๋ ๋จ์ผ ํผ ์ ์ถ ๊ณผ์ ์ธ ๊ฒ์ ๊ด๋ฆฌํ๊ธฐ ์ํด ์ธ ๊ฐ์ ๊ฐ๋ณ useState ํธ์ถ์ด ํ์ํฉ๋๋ค.
- ์๋ ์ํ ๊ด๋ฆฌ: ๊ฐ๋ฐ์๋ try...catch...finally ๋ธ๋ก ๋ด์์ ์ฌ๋ฐ๋ฅธ ์์๋ก ๋ก๋ฉ ๋ฐ ์ค๋ฅ ์ํ๋ฅผ ์๋์ผ๋ก ์ค์ ํ๊ณ ์ฌ์ค์ ํด์ผ ํ ์ฑ ์์ด ์์ต๋๋ค. ์ด๋ ๋ฐ๋ณต์ ์ด๊ณ ์ค๋ฅ๊ฐ ๋ฐ์ํ๊ธฐ ์ฝ์ต๋๋ค.
- ๊ฒฐํฉ๋: ํผ ์ ์ถ ๊ฒฐ๊ณผ๋ฅผ ์ฒ๋ฆฌํ๋ ๋ก์ง์ด ์ปดํฌ๋ํธ์ ๋ ๋๋ง ๋ก์ง๊ณผ ๋ฐ์ ํ๊ฒ ๊ฒฐํฉ๋์ด ์์ต๋๋ค.
useActionState ์๊ฐ: ํจ๋ฌ๋ค์์ ์ ํ
useActionState๋ ํผ ์ ์ถ๊ณผ ๊ฐ์ ๋น๋๊ธฐ ์ก์ ์ ์ํ๋ฅผ ๊ด๋ฆฌํ๊ธฐ ์ํด ํน๋ณํ ์ค๊ณ๋ ๋ฆฌ์กํธ ํ ์ ๋๋ค. ์ํ๋ฅผ ์ก์ ํจ์์ ๊ฒฐ๊ณผ์ ์ง์ ์ฐ๊ฒฐํ์ฌ ์ ์ฒด ๊ณผ์ ์ ๊ฐ์ํํฉ๋๋ค.
์๊ทธ๋์ฒ๋ ๋ช ํํ๊ณ ๊ฐ๊ฒฐํฉ๋๋ค:
const [state, formAction] = useActionState(actionFn, initialState);
๊ตฌ์ฑ ์์๋ฅผ ๋ถ์ํด ๋ณด๊ฒ ์ต๋๋ค:
actionFn(previousState, formData)
: ์์ ์ ์ํํ๋ ๋น๋๊ธฐ ํจ์์ ๋๋ค(์: API ํธ์ถ). ์ด์ ์ํ์ ํผ ๋ฐ์ดํฐ๋ฅผ ์ธ์๋ก ๋ฐ์ต๋๋ค. ๊ฒฐ์ ์ ์ผ๋ก, ์ด ํจ์๊ฐ ๋ฐํํ๋ ๊ฒ์ด ์๋ก์ด ์ํ๊ฐ ๋ฉ๋๋ค.initialState
: ์ก์ ์ด ์ฒ์ ์คํ๋๊ธฐ ์ ์ ์ํ ๊ฐ์ ๋๋ค.state
: ํ์ฌ ์ํ์ ๋๋ค. ์ฒ์์๋ initialState๋ฅผ ๊ฐ์ง๋ฉฐ, ๊ฐ ์คํ ํ actionFn์ ๋ฐํ ๊ฐ์ผ๋ก ์ ๋ฐ์ดํธ๋ฉ๋๋ค.formAction
: ์ก์ ํจ์๋ฅผ ๊ฐ์ผ ์๋ก์ด ๋ฒ์ ์ ํจ์์ ๋๋ค. ์ด ํจ์๋ฅผ<form>
์์์action
prop์ ์ ๋ฌํด์ผ ํฉ๋๋ค. ๋ฆฌ์กํธ๋ ์ด ๊ฐ์ธ์ง ํจ์๋ฅผ ์ฌ์ฉํ์ฌ ์ก์ ์ ๋ณด๋ฅ ์ํ๋ฅผ ์ถ์ ํฉ๋๋ค.
์ค์ฉ ์์ : useActionState๋ก ๋ฆฌํฉํ ๋งํ๊ธฐ
์ด์ useActionState๋ฅผ ์ฌ์ฉํ์ฌ ์ ํ ํผ์ ๋ฆฌํฉํ ๋งํด ๋ด ์๋ค. ๊ฐ์ ์ ์ด ์ฆ์ ๋๋ฌ๋ฉ๋๋ค.
๋จผ์ , ์ก์ ๋ก์ง์ ์กฐ์ ํด์ผ ํฉ๋๋ค. ์ค๋ฅ๋ฅผ ๋์ง๋ ๋์ , ์ก์ ์ ๊ฒฐ๊ณผ๋ฅผ ์ค๋ช ํ๋ ์ํ ๊ฐ์ฒด๋ฅผ ๋ฐํํด์ผ ํฉ๋๋ค.
์์: useActionState๋ฅผ ์ฌ์ฉํ '์๋ก์ด' ๋ฐฉ๋ฒ
// useActionState์ ํจ๊ป ์๋ํ๋๋ก ์ค๊ณ๋ ์ก์
ํจ์
const addProductAction = async (previousState, formData) => {
const productName = formData.get('productName');
await new Promise(resolve => setTimeout(resolve, 1500)); // ๋คํธ์ํฌ ์ง์ฐ ์๋ฎฌ๋ ์ด์
if (!productName || productName.length < 3) {
return { message: '์ ํ๋ช
์ 3๊ธ์ ์ด์์ด์ด์ผ ํฉ๋๋ค.', success: false };
}
console.log(`"${productName}" ์ ํ์ด ์ถ๊ฐ๋์์ต๋๋ค.`);
// ์ฑ๊ณต ์, ์ฑ๊ณต ๋ฉ์์ง๋ฅผ ๋ฐํํ๊ณ ํผ์ ๋น์๋๋ค.
return { message: `"${productName}"์(๋ฅผ) ์ฑ๊ณต์ ์ผ๋ก ์ถ๊ฐํ์ต๋๋ค.`, success: true };
};
// ๋ฆฌํฉํ ๋ง๋ ์ปดํฌ๋ํธ
{state.message} {state.message}import { useActionState } from 'react';
// ์ฐธ๊ณ : ๋ค์ ์น์
์์ ๋ณด๋ฅ ์ํ๋ฅผ ์ฒ๋ฆฌํ๊ธฐ ์ํด useFormStatus๋ฅผ ์ถ๊ฐํ ๊ฒ์
๋๋ค.
function NewProductForm() {
const initialState = { message: null, success: false };
const [state, formAction] = useActionState(addProductAction, initialState);
return (
{!state.success && state.message && (
)}
{state.success && state.message && (
)}
);
}
์ผ๋ง๋ ๊น๋ํด์ก๋์ง ๋ณด์ธ์! ์ธ ๊ฐ์ useState ํ ์ ๋จ์ผ useActionState ํ ์ผ๋ก ๋์ฒดํ์ต๋๋ค. ์ด์ ์ปดํฌ๋ํธ์ ์ฑ ์์ ์์ ํ `state` ๊ฐ์ฒด๋ฅผ ๊ธฐ๋ฐ์ผ๋ก UI๋ฅผ ๋ ๋๋งํ๋ ๊ฒ์ ๋๋ค. ๋ชจ๋ ๋น์ฆ๋์ค ๋ก์ง์ `addProductAction` ํจ์ ๋ด์ ๊น๋ํ๊ฒ ์บก์ํ๋์ด ์์ต๋๋ค. ์ํ๋ ์ก์ ์ด ๋ฐํํ๋ ๊ฒ์ ๋ฐ๋ผ ์๋์ผ๋ก ์ ๋ฐ์ดํธ๋ฉ๋๋ค.
ํ์ง๋ง ์ ๊น, ๋ณด๋ฅ ์ํ๋ ์ด๋ป๊ฒ ํ์ฃ ? ํผ์ด ์ ์ถ๋๋ ๋์ ๋ฒํผ์ ์ด๋ป๊ฒ ๋นํ์ฑํํ ๊น์?
useFormStatus๋ก ๋ณด๋ฅ ์ํ ์ฒ๋ฆฌํ๊ธฐ
๋ฆฌ์กํธ๋ ์ด ๋ฌธ์ ๋ฅผ ํด๊ฒฐํ๊ธฐ ์ํด ์ค๊ณ๋ ๋๋ฐ ํ
์ธ useFormStatus๋ฅผ ์ ๊ณตํฉ๋๋ค. ์ด ํ
์ ๋ง์ง๋ง ํผ ์ ์ถ์ ๋ํ ์ํ ์ ๋ณด๋ฅผ ์ ๊ณตํ์ง๋ง, ์ค์ํ ๊ท์น์ด ์์ต๋๋ค: ์ํ๋ฅผ ์ถ์ ํ๋ ค๋ <form>
๋ด๋ถ์ ๋ ๋๋ง๋๋ ์ปดํฌ๋ํธ์์ ํธ์ถํด์ผ ํฉ๋๋ค.
์ด๋ ๊ด์ฌ์ฌ์ ๋ถ๋ฆฌ๋ฅผ ์ฅ๋ คํฉ๋๋ค. ์ ์ถ ๋ฒํผ๊ณผ ๊ฐ์ด ํผ์ ์ ์ถ ์ํ๋ฅผ ์์์ผ ํ๋ UI ์์๋ฅผ ์ํ ์ปดํฌ๋ํธ๋ฅผ ํน๋ณํ ๋ง๋ญ๋๋ค.
useFormStatus ํ ์ ์ฌ๋ฌ ์์ฑ์ ๊ฐ์ง ๊ฐ์ฒด๋ฅผ ๋ฐํํ๋ฉฐ, ๊ทธ์ค ๊ฐ์ฅ ์ค์ํ ๊ฒ์ `pending`์ ๋๋ค.
const { pending, data, method, action } = useFormStatus();
pending
: ๋ถ๋ชจ ํผ์ด ํ์ฌ ์ ์ถ ์ค์ด๋ฉด `true`, ๊ทธ๋ ์ง ์์ผ๋ฉด `false`์ธ ๋ถ๋ฆฌ์ธ ๊ฐ์ ๋๋ค.data
: ์ ์ถ ์ค์ธ ๋ฐ์ดํฐ๋ฅผ ํฌํจํ๋ `FormData` ๊ฐ์ฒด์ ๋๋ค.method
: HTTP ๋ฉ์๋๋ฅผ ๋ํ๋ด๋ ๋ฌธ์์ด์ ๋๋ค (`'get'` ๋๋ `'post'`).action
: ํผ์ `action` prop์ ์ ๋ฌ๋ ํจ์์ ๋ํ ์ฐธ์กฐ์ ๋๋ค.
์ํ๋ฅผ ์ธ์งํ๋ ์ ์ถ ๋ฒํผ ๋ง๋ค๊ธฐ
์ ์ฉ `SubmitButton` ์ปดํฌ๋ํธ๋ฅผ ๋ง๋ค์ด ํผ์ ํตํฉํด ๋ด ์๋ค.
์์: SubmitButton ์ปดํฌ๋ํธ
import { useFormStatus } from 'react-dom';
// ์ฐธ๊ณ : useFormStatus๋ 'react'๊ฐ ์๋ 'react-dom'์์ ๊ฐ์ ธ์ต๋๋ค.
function SubmitButton() {
const { pending } = useFormStatus();
return (
{pending ? '์ถ๊ฐ ์ค...' : '์ ํ ์ถ๊ฐ'}
);
}
์ด์ ๋ฉ์ธ ํผ ์ปดํฌ๋ํธ๋ฅผ ์ ๋ฐ์ดํธํ์ฌ ์ฌ์ฉํด ๋ด ์๋ค.
์์: useActionState์ useFormStatus๋ฅผ ์ฌ์ฉํ ์์ ํ ํผ
{state.message} {state.message}import { useActionState } from 'react';
import { useFormStatus } from 'react-dom';
// ... (addProductAction ํจ์๋ ๋์ผํ๊ฒ ์ ์ง)
function SubmitButton() { /* ... ์์์ ์ ์ํ ๋๋ก ... */ }
function CompleteProductForm() {
const initialState = { message: null, success: false };
const [state, formAction] = useActionState(addProductAction, initialState);
return (
{/* ์ฑ๊ณต ์ ์
๋ ฅ์ ์ด๊ธฐํํ๊ธฐ ์ํด key๋ฅผ ์ถ๊ฐํ ์ ์์ต๋๋ค */}
{!state.success && state.message && (
)}
{state.success && state.message && (
)}
);
}
์ด ๊ตฌ์กฐ๋ฅผ ์ฌ์ฉํ๋ฉด `CompleteProductForm` ์ปดํฌ๋ํธ๋ ๋ณด๋ฅ ์ํ์ ๋ํด ์๋ฌด๊ฒ๋ ์ ํ์๊ฐ ์์ต๋๋ค. `SubmitButton`์ ์์ ํ ๋ ๋ฆฝ์ ์ ๋๋ค. ์ด ๊ตฌ์ฑ ํจํด์ ๋ณต์กํ๊ณ ์ ์ง๋ณด์ ๊ฐ๋ฅํ UI๋ฅผ ๊ตฌ์ถํ๋ ๋ฐ ๋งค์ฐ ๊ฐ๋ ฅํฉ๋๋ค.
์ ์ง์ ํฅ์์ ํ
์ด ์๋ก์ด ์ก์ ๊ธฐ๋ฐ ์ ๊ทผ ๋ฐฉ์์ ๊ฐ์ฅ ์ฌ์คํ ์ด์ ์ค ํ๋๋, ํนํ ์๋ฒ ์ก์ ๊ณผ ํจ๊ป ์ฌ์ฉํ ๋, ์๋์ ์ธ ์ ์ง์ ํฅ์(progressive enhancement)์ ๋๋ค. ์ด๋ ๋คํธ์ํฌ ์กฐ๊ฑด์ด ๋ถ์์ ํ๊ณ ์ฌ์ฉ์๊ฐ ๊ตฌํ ๊ธฐ๊ธฐ๋ฅผ ์ฌ์ฉํ๊ฑฐ๋ ์๋ฐ์คํฌ๋ฆฝํธ๋ฅผ ๋นํ์ฑํํ์ ์ ์๋ ๊ธ๋ก๋ฒ ๊ณ ๊ฐ์ ์ํ ์ ํ๋ฆฌ์ผ์ด์ ์ ๊ตฌ์ถํ๋ ๋ฐ ํ์์ ์ธ ๊ฐ๋ ์ ๋๋ค.
์๋ ๋ฐฉ์์ ๋ค์๊ณผ ๊ฐ์ต๋๋ค:
- ์๋ฐ์คํฌ๋ฆฝํธ๊ฐ ์์ ๋: ์ฌ์ฉ์์ ๋ธ๋ผ์ฐ์ ๊ฐ ํด๋ผ์ด์ธํธ ์ธก ์๋ฐ์คํฌ๋ฆฝํธ๋ฅผ ์คํํ์ง ์์ผ๋ฉด, `<form action={...}>`์ ํ์ค HTML ํผ์ผ๋ก ์๋ํฉ๋๋ค. ์๋ฒ์ ์ ์ฒด ํ์ด์ง ์์ฒญ์ ๋ณด๋ ๋๋ค. Next.js์ ๊ฐ์ ํ๋ ์์ํฌ๋ฅผ ์ฌ์ฉํ๋ ๊ฒฝ์ฐ, ์๋ฒ ์ธก ์ก์ ์ด ์คํ๋๊ณ ํ๋ ์์ํฌ๋ ์ ์ํ(์: ์ ํจ์ฑ ๊ฒ์ฌ ์ค๋ฅ ํ์)๋ก ์ ์ฒด ํ์ด์ง๋ฅผ ๋ค์ ๋ ๋๋งํฉ๋๋ค. ์ ํ๋ฆฌ์ผ์ด์ ์ SPA์ ๊ฐ์ ๋ถ๋๋ฌ์ ์์ด๋ ์๋ฒฝํ๊ฒ ์๋ํฉ๋๋ค.
- ์๋ฐ์คํฌ๋ฆฝํธ๊ฐ ์์ ๋: ์๋ฐ์คํฌ๋ฆฝํธ ๋ฒ๋ค์ด ๋ก๋๋๊ณ ๋ฆฌ์กํธ๊ฐ ํ์ด์ง๋ฅผ ํ์ด๋๋ ์ด์ ํ๋ฉด, ๋์ผํ `formAction`์ด ํด๋ผ์ด์ธํธ ์ธก์์ ์คํ๋ฉ๋๋ค. ์ ์ฒด ํ์ด์ง ๋ฆฌ๋ก๋ ๋์ , ์ผ๋ฐ์ ์ธ fetch ์์ฒญ์ฒ๋ผ ๋์ํฉ๋๋ค. ์ก์ ์ด ํธ์ถ๋๊ณ , ์ํ๊ฐ ์ ๋ฐ์ดํธ๋๋ฉฐ, ์ปดํฌ๋ํธ์ ํ์ํ ๋ถ๋ถ๋ง ๋ค์ ๋ ๋๋ง๋ฉ๋๋ค.
์ด๋ ํผ ๋ก์ง์ ํ ๋ฒ๋ง ์์ฑํ๋ฉด ๋ ์๋๋ฆฌ์ค ๋ชจ๋์์ ์ํํ๊ฒ ์๋ํ๋ค๋ ๊ฒ์ ์๋ฏธํฉ๋๋ค. ๊ธฐ๋ณธ์ ์ผ๋ก ๊ฒฌ๊ณ ํ๊ณ ์ ๊ทผ์ฑ ๋์ ์ ํ๋ฆฌ์ผ์ด์ ์ ๊ตฌ์ถํ๊ฒ ๋๋ฉฐ, ์ด๋ ์ ์ธ๊ณ ์ฌ์ฉ์ ๊ฒฝํ์ ํฐ ์ด์ ์ ๋๋ค.
๊ณ ๊ธ ํจํด ๋ฐ ์ฌ์ฉ ์ฌ๋ก
1. ์๋ฒ ์ก์ vs. ํด๋ผ์ด์ธํธ ์ก์
useActionState์ ์ ๋ฌํ๋ `actionFn`์ ์์ ์์์ฒ๋ผ ํ์ค ํด๋ผ์ด์ธํธ ์ธก ๋น๋๊ธฐ ํจ์์ผ ์๋ ์๊ณ , ์๋ฒ ์ก์
์ผ ์๋ ์์ต๋๋ค. ์๋ฒ ์ก์
์ ์๋ฒ์ ์ ์๋์ด ํด๋ผ์ด์ธํธ ์ปดํฌ๋ํธ์์ ์ง์ ํธ์ถํ ์ ์๋ ํจ์์
๋๋ค. Next.js์ ๊ฐ์ ํ๋ ์์ํฌ์์๋ ํจ์ ๋ณธ๋ฌธ ์๋จ์ "use server";
์ง์์ด๋ฅผ ์ถ๊ฐํ์ฌ ์ ์ํฉ๋๋ค.
- ํด๋ผ์ด์ธํธ ์ก์ : ํด๋ผ์ด์ธํธ ์ธก ์ํ์๋ง ์ํฅ์ ๋ฏธ์น๊ฑฐ๋ ํด๋ผ์ด์ธํธ์์ ์ง์ ํ์ฌ API๋ฅผ ํธ์ถํ๋ ๋ณ๊ฒฝ ์์ ์ ์ด์์ ์ ๋๋ค.
- ์๋ฒ ์ก์ : ๋ฐ์ดํฐ๋ฒ ์ด์ค๋ ๋ค๋ฅธ ์๋ฒ ์ธก ๋ฆฌ์์ค๋ฅผ ํฌํจํ๋ ๋ณ๊ฒฝ ์์ ์ ์๋ฒฝํฉ๋๋ค. ๋ชจ๋ ๋ณ๊ฒฝ ์์ ์ ๋ํด ์๋์ผ๋ก API ์๋ํฌ์ธํธ๋ฅผ ๋ง๋ค ํ์๊ฐ ์์ด ์ํคํ ์ฒ๋ฅผ ๋จ์ํํฉ๋๋ค.
๊ฐ์ฅ ํฐ ์ฅ์ ์ useActionState๊ฐ ๋ ๊ฐ์ง ๋ชจ๋์ ๋์ผํ๊ฒ ์๋ํ๋ค๋ ๊ฒ์ ๋๋ค. ์ปดํฌ๋ํธ ์ฝ๋๋ฅผ ๋ณ๊ฒฝํ์ง ์๊ณ ๋ ํด๋ผ์ด์ธํธ ์ก์ ์ ์๋ฒ ์ก์ ์ผ๋ก ๊ต์ฒดํ ์ ์์ต๋๋ค.
2. `useOptimistic`์ ์ด์ฉํ ๋๊ด์ ์ ๋ฐ์ดํธ
๋์ฑ ๋ฐ์์ ์ธ ๋๋์ ์ฃผ๊ธฐ ์ํด useActionState๋ฅผ useOptimistic ํ ๊ณผ ๊ฒฐํฉํ ์ ์์ต๋๋ค. ๋๊ด์ ์ ๋ฐ์ดํธ๋ ๋น๋๊ธฐ ์ก์ ์ด ์ฑ๊ณตํ ๊ฒ์ด๋ผ๊ณ *๊ฐ์ *ํ๊ณ UI๋ฅผ ์ฆ์ ์ ๋ฐ์ดํธํ๋ ๊ฒ์ ๋๋ค. ๋ง์ฝ ์คํจํ๋ฉด, UI๋ฅผ ์ด์ ์ํ๋ก ๋๋๋ฆฝ๋๋ค.
๋๊ธ์ ์ถ๊ฐํ๋ ์์ ๋ฏธ๋์ด ์ฑ์ ์์ํด ๋ณด์ธ์. ๋๊ด์ ์ผ๋ก, ์๋ฒ์ ์์ฒญ์ ๋ณด๋ด๋ ๋์ ์ ๋๊ธ์ ๋ชฉ๋ก์ ์ฆ์ ํ์ํ ๊ฒ์ ๋๋ค. useOptimistic์ ์ด ํจํด์ ์ฝ๊ฒ ๊ตฌํํ ์ ์๋๋ก ์ก์ ๊ณผ ํจ๊ป ์๋ํ๋๋ก ์ค๊ณ๋์์ต๋๋ค.
3. ์ฑ๊ณต ์ ํผ ์ด๊ธฐํ
์ฑ๊ณต์ ์ธ ์ ์ถ ํ ํผ ์ ๋ ฅ์ ์ง์ฐ๋ ๊ฒ์ ์ผ๋ฐ์ ์ธ ์๊ตฌ ์ฌํญ์ ๋๋ค. useActionState๋ฅผ ์ฌ์ฉํ์ฌ ์ด๋ฅผ ๋ฌ์ฑํ๋ ๋ช ๊ฐ์ง ๋ฐฉ๋ฒ์ด ์์ต๋๋ค.
- Key Prop ํธ๋ฆญ: `CompleteProductForm` ์์ ์์ ๋ณด์ฌ์ค ๊ฒ์ฒ๋ผ, ์ ๋ ฅ์ด๋ ์ ์ฒด ํผ์ ๊ณ ์ ํ `key`๋ฅผ ํ ๋นํ ์ ์์ต๋๋ค. ํค๊ฐ ๋ณ๊ฒฝ๋๋ฉด ๋ฆฌ์กํธ๋ ์ด์ ์ปดํฌ๋ํธ๋ฅผ ๋ง์ดํธ ํด์ ํ๊ณ ์ ์ปดํฌ๋ํธ๋ฅผ ๋ง์ดํธํ์ฌ ์ฌ์ค์ ์ํ๋ฅผ ์ด๊ธฐํํฉ๋๋ค. ํค๋ฅผ ์ฑ๊ณต ํ๋๊ทธ(`key={state.success ? 'success' : 'initial'}`)์ ์ฐ๊ฒฐํ๋ ๊ฒ์ ๊ฐ๋จํ๊ณ ํจ๊ณผ์ ์ธ ๋ฐฉ๋ฒ์ ๋๋ค.
- ์ ์ด ์ปดํฌ๋ํธ: ํ์ํ ๊ฒฝ์ฐ ์ฌ์ ํ ์ ์ด ์ปดํฌ๋ํธ๋ฅผ ์ฌ์ฉํ ์ ์์ต๋๋ค. useState๋ก ์ ๋ ฅ ๊ฐ์ ๊ด๋ฆฌํจ์ผ๋ก์จ, useActionState์ ์ฑ๊ณต ์ํ๋ฅผ ๊ฐ์งํ๋ useEffect ๋ด์์ ์ธํฐ ํจ์๋ฅผ ํธ์ถํ์ฌ ๊ฐ์ ์ง์ธ ์ ์์ต๋๋ค.
ํํ ํจ์ ๊ณผ ๋ชจ๋ฒ ์ฌ๋ก
useFormStatus
์ ์์น: useFormStatus๋ฅผ ํธ์ถํ๋ ์ปดํฌ๋ํธ๋ ๋ฐ๋์ `<form>`์ ์์์ผ๋ก ๋ ๋๋ง๋์ด์ผ ํฉ๋๋ค. ํ์ ๋ ๋ถ๋ชจ ์ปดํฌ๋ํธ์์๋ ์๋ํ์ง ์์ต๋๋ค.- ์ง๋ ฌํ ๊ฐ๋ฅํ ์ํ: ์๋ฒ ์ก์ ์ ์ฌ์ฉํ ๋, ์ก์ ์์ ๋ฐํ๋๋ ์ํ ๊ฐ์ฒด๋ ์ง๋ ฌํ ๊ฐ๋ฅํด์ผ ํฉ๋๋ค. ์ฆ, ํจ์, ์ฌ๋ณผ ๋๋ ๊ธฐํ ์ง๋ ฌํ ๋ถ๊ฐ๋ฅํ ๊ฐ์ ํฌํจํ ์ ์์ต๋๋ค. ์ผ๋ฐ ๊ฐ์ฒด, ๋ฐฐ์ด, ๋ฌธ์์ด, ์ซ์, ๋ถ๋ฆฌ์ธ ๊ฐ์ ์ฌ์ฉํ์ธ์.
- ์ก์ ์์ ์ค๋ฅ ๋์ง์ง ์๊ธฐ: `throw new Error()` ๋์ , ์ก์ ํจ์๋ ์ค๋ฅ๋ฅผ ์ฐ์ํ๊ฒ ์ฒ๋ฆฌํ๊ณ ์ค๋ฅ๋ฅผ ์ค๋ช ํ๋ ์ํ ๊ฐ์ฒด๋ฅผ ๋ฐํํด์ผ ํฉ๋๋ค(์: `{ success: false, message: '์ค๋ฅ๊ฐ ๋ฐ์ํ์ต๋๋ค.' }`). ์ด๋ ๊ฒ ํ๋ฉด ์ํ๊ฐ ํญ์ ์์ธก ๊ฐ๋ฅํ๊ฒ ์ ๋ฐ์ดํธ๋ฉ๋๋ค.
- ๋ช
ํํ ์ํ ํํ ์ ์: ์ฒ์๋ถํฐ ์ํ ๊ฐ์ฒด์ ๋ํ ์ผ๊ด๋ ๊ตฌ์กฐ๋ฅผ ํ๋ฆฝํ์ธ์. `{ data: T | null, message: string | null, success: boolean, errors: Record
| null }`์ ๊ฐ์ ํํ๋ ๋ง์ ์ฌ์ฉ ์ฌ๋ก๋ฅผ ํฌ๊ดํ ์ ์์ต๋๋ค.
useActionState vs. useReducer: ๊ฐ๋จ ๋น๊ต
์ธ๋ป ๋ณด๊ธฐ์ useActionState๋ useReducer์ ๋น์ทํด ๋ณด์ผ ์ ์์ต๋๋ค. ๋ ๋ค ์ด์ ์ํ๋ฅผ ๊ธฐ๋ฐ์ผ๋ก ์ํ๋ฅผ ์ ๋ฐ์ดํธํ๊ธฐ ๋๋ฌธ์ ๋๋ค. ๊ทธ๋ฌ๋ ๊ทธ๋ค์ ๋ค๋ฅธ ๋ชฉ์ ์ ๊ฐ์ง๋๋ค.
useReducer
๋ ํด๋ผ์ด์ธํธ ์ธก์์ ๋ณต์กํ ์ํ ์ ํ์ ๊ด๋ฆฌํ๊ธฐ ์ํ ๋ฒ์ฉ ํ ์ ๋๋ค. ์ก์ ์ ๋์คํจ์นํ์ฌ ํธ๋ฆฌ๊ฑฐ๋๋ฉฐ, ๊ฐ๋ฅ์ฑ ์๋ ๋๊ธฐ์ ์ํ ๋ณ๊ฒฝ์ด ๋ง์ ์ํ ๋ก์ง(์: ๋ณต์กํ ๋ค๋จ๊ณ ๋ง๋ฒ์ฌ)์ ์ด์์ ์ ๋๋ค.useActionState
๋ ๋จ์ผ, ์ผ๋ฐ์ ์ผ๋ก ๋น๋๊ธฐ ์ก์ ์ ์๋ตํ์ฌ ๋ณ๊ฒฝ๋๋ ์ํ๋ฅผ ์ํด ์ค๊ณ๋ ์ ๋ฌธํ๋ ํ ์ ๋๋ค. ์ฃผ์ ์ญํ ์ HTML ํผ, ์๋ฒ ์ก์ , ๊ทธ๋ฆฌ๊ณ ๋ณด๋ฅ ์ํ ์ ํ๊ณผ ๊ฐ์ ๋ฆฌ์กํธ์ ๋์์ฑ ๋ ๋๋ง ๊ธฐ๋ฅ๊ณผ ํตํฉํ๋ ๊ฒ์ ๋๋ค.
ํต์ฌ: ํผ ์ ์ถ ๋ฐ ํผ์ ์ฐ๊ฒฐ๋ ๋น๋๊ธฐ ์์ ์๋ useActionState๊ฐ ํ๋์ ์ด๊ณ ๋ชฉ์ ์ ๋ง๊ฒ ์ ์๋ ๋๊ตฌ์ ๋๋ค. ๋ค๋ฅธ ๋ณต์กํ ํด๋ผ์ด์ธํธ ์ธก ์ํ ๋จธ์ ์๋ useReducer๊ฐ ์ฌ์ ํ ํ๋ฅญํ ์ ํ์ ๋๋ค.
๊ฒฐ๋ก : ๋ฆฌ์กํธ ํผ์ ๋ฏธ๋๋ฅผ ๋ง์ดํ๋ฉฐ
useActionState ํ ์ ์๋ก์ด API ๊ทธ ์ด์์ ๋๋ค. ์ด๋ ๋ฆฌ์กํธ์์ ํผ๊ณผ ๋ฐ์ดํฐ ๋ณ๊ฒฝ์ ์ฒ๋ฆฌํ๋ ๋ ๊ฒฌ๊ณ ํ๊ณ , ์ ์ธ์ ์ด๋ฉฐ, ์ฌ์ฉ์ ์ค์ฌ์ ์ธ ๋ฐฉ์์ผ๋ก์ ๊ทผ๋ณธ์ ์ธ ์ ํ์ ๋ํ๋ ๋๋ค. ์ด๋ฅผ ์ฑํํจ์ผ๋ก์จ ๋ค์๊ณผ ๊ฐ์ ์ด์ ์ ์ป์ ์ ์์ต๋๋ค:
- ๋ณด์ผ๋ฌํ๋ ์ดํธ ๊ฐ์: ๋จ์ผ ํ ์ด ์ฌ๋ฌ useState ํธ์ถ๊ณผ ์๋ ์ํ ์กฐ์ ์ ๋์ฒดํฉ๋๋ค.
- ํตํฉ๋ ๋ณด๋ฅ ์ํ: ๋๋ฐ ํ ์ธ useFormStatus๋ฅผ ์ฌ์ฉํ์ฌ ๋ก๋ฉ UI๋ฅผ ์ํํ๊ฒ ์ฒ๋ฆฌํฉ๋๋ค.
- ๋ด์ฅ๋ ์ ์ง์ ํฅ์: ์๋ฐ์คํฌ๋ฆฝํธ ์ ๋ฌด์ ๊ด๊ณ์์ด ์๋ํ๋ ์ฝ๋๋ฅผ ์์ฑํ์ฌ ๋ชจ๋ ์ฌ์ฉ์์ ๋ํ ์ ๊ทผ์ฑ๊ณผ ๋ณต์๋ ฅ์ ๋ณด์ฅํฉ๋๋ค.
- ๋จ์ํ๋ ์๋ฒ ํต์ : ์๋ฒ ์ก์ ๊ณผ ์์ฐ์ค๋ฝ๊ฒ ๋ง์๋จ์ด์ ธ ํ์คํ ๊ฐ๋ฐ ๊ฒฝํ์ ๊ฐ์ํํฉ๋๋ค.
์๋ก์ด ํ๋ก์ ํธ๋ฅผ ์์ํ๊ฑฐ๋ ๊ธฐ์กด ํ๋ก์ ํธ๋ฅผ ๋ฆฌํฉํ ๋งํ ๋ useActionState๋ฅผ ์ฌ์ฉํด ๋ณด์ธ์. ์ฝ๋๋ฅผ ๋ ๊น๋ํ๊ณ ์์ธก ๊ฐ๋ฅํ๊ฒ ๋ง๋ค์ด ๊ฐ๋ฐ์ ๊ฒฝํ์ ํฅ์์ํฌ ๋ฟ๋ง ์๋๋ผ, ๋ ๋น ๋ฅด๊ณ , ๋ ํ๋ ฅ์ ์ด๋ฉฐ, ๋ค์ํ ์ ์ธ๊ณ ์ฌ์ฉ์์๊ฒ ์ ๊ทผ ๊ฐ๋ฅํ ๊ณ ํ์ง ์ ํ๋ฆฌ์ผ์ด์ ์ ๊ตฌ์ถํ ์ ์๋๋ก ํ์ ์ค์ด์ค ๊ฒ์ ๋๋ค.